#include "mmp_lib.h"
#include "config_fw.h"
#if SUPPORT_UAC
#if (EXT_CODEC_SUPPORT == 1)
#if (AUDEXT_DAC == ADC3101)
#include "lib_retina.h"
#include "mmp_reg_gbl.h"
#include "mmpd_system.h"
#include "mmps_audio.h"
#include "ADC3101.h"
#include "mmpf_i2cm.h"
//#include "mmp_reg_audio.h"
#include "mmpf_usbuac.h"

static MMPF_I2CM_ATTRIBUTE m_ADC3101_I2cmAttribute = {MMPF_I2CM_ID_0, ADC3101_I2C_DEV_ADDR, 8, 8, 0, MMP_FALSE, MMP_FALSE, MMP_FALSE, MMP_FALSE, 0, 0, 0, MMPF_I2CM_SPEED_HW_250K, NULL, NULL/*MMPF_PIO_REG_GPIO16, MMPF_PIO_REG_GPIO17*/};
static MMP_BOOL m_bCodecInitialized = MMP_FALSE;
//static MMP_BOOL m_bCodecAsMaster = MMP_TRUE;
static AUDIO_EXTCODEC_PATH m_ulCodecPath = AUDIO_EXT_PATH_MAX;

static MMP_USHORT current_page= 0xFF;
static const struct ADC3101_PLL_Setting ADC3101_SampleRate_Setting[] = {

	//{rate, p_val(=P =R), pll_j, pll_d, aosr, nadc, madc, bclk_N}
	
	//		MCLK * pll_j.pll_d * R	//
	// Fs = ----------------------	//
	//		nadc * madc * aosr * P	//
	
	/* 8k rate */
	{8000, 1, 8, 1920, 128, 12, 8, 32},
	/* 11.025k rate */
	{11025, 1, 7, 5264, 128, 8, 8, 16},
	/* 16k rate */
	{16000, 1, 8, 1920, 128, 6, 8, 32},
	/* 22.05k rate */
	{22050, 1, 7, 5264, 128, 4, 8, 8},
	/* 32k rate */
	{32000, 1, 8, 1920, 128, 3, 8, 32},
	/* 44.1k rate */
	{44100, 1, 7, 5264, 128, 2, 8, 8},
	/* 48k rate */
	{48000, 1, 8, 1920, 128, 2, 8, 16},
};

#define I2S_SLAVE_MODE  1

//*----------------------------------------------------------------------------
// function :MMPF_ADC3101_WriteReg
// input    : (Register address, Register data), reg 7 bits, data 9 bits
// output   : none
// descript : write codec register
//*----------------------------------------------------------------------------
void ADC3101_ReadReg(MMP_USHORT page_number, MMP_USHORT usReg, MMP_USHORT *usData)
{
    MMP_ERR ret = MMP_ERR_NONE;
    
    if(current_page != page_number)
    {
        ret = MMPF_I2cm_WriteReg(&m_ADC3101_I2cmAttribute, 0, page_number);
        #if 0
        RTNA_DBG_Str0("Chage to Page:");
        RTNA_DBG_Long0(page_number);
        RTNA_DBG_Str0("\r\n");
        #endif
        if(ret == MMP_ERR_NONE)
            current_page = page_number;
    }
    
    MMPF_I2cm_ReadReg(&m_ADC3101_I2cmAttribute, usReg, usData);
    return;
}

//*----------------------------------------------------------------------------
// function :MMPF_ADC3101_WriteReg
// input    : (Register address, Register data), reg 7 bits, data 9 bits
// output   : none
// descript : write codec register
//*----------------------------------------------------------------------------
void ADC3101_WriteReg(MMP_USHORT page_number, MMP_USHORT usReg, MMP_USHORT usData)
{
    MMP_ERR ret = MMP_ERR_NONE;
    
    if(current_page != page_number)
    {
        ret = MMPF_I2cm_WriteReg(&m_ADC3101_I2cmAttribute, 0, page_number);
        #if 0
        RTNA_DBG_Str0("Chage to Page:");
        RTNA_DBG_Long0(page_number);
        RTNA_DBG_Str0("\r\n");
        #endif
        if(ret == MMP_ERR_NONE)
            current_page = page_number;
    }
    
    MMPF_I2cm_WriteReg(&m_ADC3101_I2cmAttribute, usReg, usData);
    
    //MMPF_I2cm_ReadReg(&m_ADC3101_I2cmAttribute, usReg, &usData);
    //dbg_printf(0, "dataOut:%x\r\n", usData);
    return;
}

//*----------------------------------------------------------------------------
// function : InitExtMicIn
// input    : none
// output   : none
// descript : Initial external mic in
//*----------------------------------------------------------------------------
#if (CUSTOMER == PNS)
extern MMP_UBYTE SEN_EN;
#endif
void InitExtMicIn(void)
{
	AITPS_GBL   pGBL = AITC_BASE_GBL;
	pGBL->GBL_IO_CTL8 = 0;
	
	#if (CUSTOMER == PNS)
	if(!SEN_EN){
		GPIO_OutputControl(SEN_POW, TRUE);
	}
	MMPF_OS_Sleep_MS(1);
	GPIO_OutputControl(TI_RES, TRUE);
	#endif
	
	MMPF_I2cm_Initialize(&m_ADC3101_I2cmAttribute);

    RTNA_DBG_Str0("InitExtMicIn \r\n");

    /*Mic*/
    //Software reset, wait 100ms for codec do the reset, then input other settings.
    ADC3101_WriteReg(0, ADC3101_HARDWARE_RESET,0x01);
    MMPF_OS_Sleep_MS(200);

	ADC3101_WriteReg(0, ADC3101_PLL_SETTING1,0x03);
    ADC3101_WriteReg(0, ADC3101_PLL_SETTING2,0x91);
    ADC3101_WriteReg(0, ADC3101_PLL_SETTING3,0x04);
    ADC3101_WriteReg(0, ADC3101_PLL_SETTING4,0x00);
    ADC3101_WriteReg(0, ADC3101_PLL_SETTING5,0x00);
	ADC3101_WriteReg(0, 0x12,0x81);	//MCLK=12.288MHz, SR=16KH
	ADC3101_WriteReg(0, 0x13,0x86);	//NADC=1, MADC=2, AOSR=128, MCLK=Codec_CLKin
	
	//Audio Setting
	ADC3101_WriteReg(0, 0x1B,0xCC);	//I2S 16bit mode
	ADC3101_WriteReg(0, 0x3D,0x01);	//Using PRB mode, P1
	ADC3101_WriteReg(0, 0x53,0x00);	//LADC volume=0dB
	ADC3101_WriteReg(0, 0x54,0x00);	//RADC volume=0dB

}
//*----------------------------------------------------------------------------
// function : PowerUpExtMicIn
// input    : none
// output   : none
// descript : Poweron external mic in
//*----------------------------------------------------------------------------
void PowerUpExtMicIn(void)
{
    RTNA_DBG_Str0("PowerUpExtMicIn \r\n");
    
    ADC3101_WriteReg(1, 0x1A,0x00);
    
    //Power-up MIC BIA
    ADC3101_WriteReg(1, 0x33,0x28);
#if (MODEL == 0) //EVB daughter board config
    //IN1L as single ended for LADC
    ADC3101_WriteReg(1, 0x34,0xFC);	//EVB: 0xFC
    
    //IN1R as single ended for RADC
    ADC3101_WriteReg(1, 0x37,0xFC);	//EVB: 0xFC
#else
    //IN1L as single ended for LADC
    ADC3101_WriteReg(1, 0x34,0x3F);	//EVB: 0xFC
    
    //IN1R as single ended for RADC
    ADC3101_WriteReg(1, 0x37,0x3F);	//EVB: 0xFC
#endif
    //Unmute MICPGA
    ADC3101_WriteReg(1, 0x3B,0x00);	//LPGA=0dB
    ADC3101_WriteReg(1, 0x3C,0x00);	//RPGA=0dB

    //Power up ADC
    ADC3101_WriteReg(0, 0x51,0xC2);	//power up L&R ADC
    ADC3101_WriteReg(0, 0x52,0x00);	//Unmute L&R ADC

    MMPC_AudioExtCodec_SetRecordVolume(77);
}
//*----------------------------------------------------------------------------
// function : PowerDownExtMicIn
// input    : none
// output   : none
// descript : Power down external mic in
//*----------------------------------------------------------------------------
void PowerDownExtMicIn(void)
{
    MMP_USHORT val = 0;
    
    ADC3101_ReadReg(0, ADC3101_PLL_SETTING2, &val);
	ADC3101_WriteReg(0, ADC3101_PLL_SETTING2, (val & ~0x80));

	/* Switch off NADC Divider */
	ADC3101_ReadReg(0, ADC3101_PLL_SETTING8, &val);
	ADC3101_WriteReg(0, ADC3101_PLL_SETTING8, val & ~0x80);

	/* Switch off MADC Divider */
	ADC3101_ReadReg(0, ADC3101_PLL_SETTING9, &val);
	ADC3101_WriteReg(0, ADC3101_PLL_SETTING9, val & ~0x80);

	/* Switch off BCLK_N Divider */
	ADC3101_ReadReg(0, ADC3101_PLL_SETTING11, &val);
	ADC3101_WriteReg(0, ADC3101_PLL_SETTING11, val & ~0x80);
    RTNA_DBG_Str0("PowerDownExtMicIn \r\n");

    //Power-up MIC BIA
    ADC3101_WriteReg(1, 0x33,0x00);
    
	#if (CUSTOMER == PNS)
	GPIO_OutputControl(TI_RES, FALSE);
	if(!SEN_EN){
		GPIO_OutputControl(SEN_POW, FALSE);
	}
	#endif
    
    RTNA_DBG_Str0("PowerDownExtMicIn \r\n");
}

void AGCControl(MMP_UBYTE bEnable, MMP_USHORT usTargetLevel)
{
    //MMP_USHORT val = 0;
    
    

    /*Mic*/
    //ADC3101_WriteReg(&m_ADC3101_I2cmAttribute, ADC3101_PWR_MGMT1,0x000);
}

/** @brief Inhouse Wolfson audio CODEC initializer.

See @ref AudioInitializer for more information.
*/
MMP_BOOL AudioInitializer(MMP_USHORT cmd)
{
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_TRUE);
    if (cmd & AUDIO_INIT_OUT) {
    	//InitExtDac();
    	//PowerUpDACOu(32000);
    }

    if (cmd & AUDIO_INIT_MICIN) {
        InitExtMicIn();
        PowerUpExtMicIn();
    }
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_FALSE);

    return MMP_TRUE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetSampleRate
// input    : ulSamplerate: external codec runs in USB mode needs to change PLL 
//            according to sample rate
// output   : none
// descript : Config external codec PLL to specified sample rate,
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetSampleRate(MMP_ULONG ulSamplerate)
{
    MMP_USHORT i = 0;

    RTNA_DBG_Str(0, "MMPC_AudioExtCodec_SetSampleRate\r\n");
    RTNA_DBG_PrintLong(0,ulSamplerate);
    
    for(i = 0; i< 7; i++)
        if(ADC3101_SampleRate_Setting[i].samplerate == ulSamplerate)
            break;
    if(i == 7)
    {
        RTNA_DBG_Str(0, "Not support this sample rate\r\n");
        return MMP_ERR_NONE;
    }
    RTNA_DBG_PrintLong(0,ADC3101_SampleRate_Setting[i].samplerate);
    ADC3101_WriteReg(0, ADC3101_PLL_SETTING2,((ADC3101_SampleRate_Setting[i].p_val << 4) | 0x01) | 0x80);

	/* J value */
	ADC3101_WriteReg(0, ADC3101_PLL_SETTING3, ADC3101_SampleRate_Setting[i].pll_j);

	/* MSB & LSB for D value */
	ADC3101_WriteReg(0, ADC3101_PLL_SETTING4, (ADC3101_SampleRate_Setting[i].pll_d >> 8));
	ADC3101_WriteReg(0, ADC3101_PLL_SETTING5, (ADC3101_SampleRate_Setting[i].pll_d & 0xFF));

	/* NADC divider value */
	ADC3101_WriteReg(0, 0x12, ADC3101_SampleRate_Setting[i].nadc | 0x80);

	/* MADC divider value */
	ADC3101_WriteReg(0, 0x13, ADC3101_SampleRate_Setting[i].madc | 0x80);

	/* AOSR value */
	ADC3101_WriteReg(0, 0x14, ADC3101_SampleRate_Setting[i].aosr);
	/* BCLK N divider */
	ADC3101_WriteReg(0, 0x1E, ADC3101_SampleRate_Setting[i].bclk_N | 0x80);
	
    return MMP_ERR_NONE;
}
//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetMute
// input    : bMute: set MMP_TRUE to mute, MMP_FALSE to un-mute 
// output   : none
// descript : Config external codec mute/un-mute.
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetMute(MMP_BOOL bMute)//work
{
    MMP_USHORT usMute = 0;
    
    MMPF_I2cm_WriteReg(&m_ADC3101_I2cmAttribute, 0,0);
    MMPF_I2cm_ReadReg(&m_ADC3101_I2cmAttribute, 0x52, &usMute);
    dbg_printf(3, "usMute = 0x%x\r\n", usMute);
    
    if (bMute) {
        RTNA_DBG_Str(0, "MMPC_AudioExtCodec_SetMute\r\n");
        ADC3101_WriteReg(0, 0x52,0x88 | usMute);
    }
    else {
        RTNA_DBG_Str(0, "MMPC_AudioExtCodec_SetUnmute\r\n");
        ADC3101_WriteReg(0, 0x52,usMute & (~0x88));
    }

    return MMP_ERR_NONE;
}
//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetVolume
// input    : level: external codec volume level
// output   : none
// descript : Config external codec volume level,
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetVolume(MMP_ULONG level)
{
    return MMP_ERR_NONE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetVolume
// input    : level: external codec volume level
// output   : none
// descript : Config external codec volume level,
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetRecordVolume(MMP_LONG value)//unit is 0.1dB, 0.1dB for each step, input range is 0~-415
{
    MMP_USHORT GLim = 71;

    if(value < (77- GLim))
        value = (77- GLim);
    else if(value > 77)
        value = 77;    
        
    value = value - (77- GLim);

    ADC3101_WriteReg(1, 0x3B,value);
    ADC3101_WriteReg(1, 0x3C,value);
    dbg_printf(0, "Vol:%d\r\n", value);
    
    return MMP_ERR_NONE;
}
//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_SetPath
// input    : path: please refer MMPF_AUDIO_EXTCODEC_PATH for path can be set
// output   : none
// descript : Config external codec path if alreay iniialized, otherwise,
//            keep the path settings only.
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_SetPath(MMP_ULONG path)
{
    if (m_bCodecInitialized == MMP_TRUE) {
        //reset codec path
        switch (m_ulCodecPath) {
        case AUDIO_EXT_HP_OUT:
            AudioInitializer(AUDIO_INIT_OUT);
            break;
        case AUDIO_EXT_MIC_IN:
            AudioInitializer(AUDIO_INIT_MICIN);
            break;
        case AUDIO_EXT_AUX_IN:
            AudioInitializer(AUDIO_INIT_LINEIN);
            break;
        case AUDIO_EXT_AUX_BYPASS_HP:
            AudioInitializer(AUDIO_INIT_OUT_LINEIN);
            break;
        default:
            return MMP_AUDIO_ERR_PARAMETER;
            break;
        }
    }
    m_ulCodecPath = path;

    return MMP_ERR_NONE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_Init
// input    : samplerate: Sample rate
//            bSetAsMaster: Set codec as master mode or slave mode
// output   : none
// descript : Initialize and power up codec according to path settings,
//            be careful, please call MMPC_AudioExtCodec_SetPath first to set path
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_Init(MMP_ULONG samplerate, MMP_ULONG ulFixedMclkFreq)
{
    if (m_ulCodecPath == AUDIO_EXT_PATH_MAX) {
        RTNA_DBG_Str0("Please set path first before init codec!\r\n");
        return MMP_AUDIO_ERR_INVALID_FLOW;
    }
    //if (I2SConfig->workingMode == MMPS_AUDIO_I2S_MASTER_MODE) {
        //set AIT as master, set codec as slave
        //m_bCodecAsMaster = MMP_FALSE;
    //}
   // else {
        //set AIT as slave, set codec as master
    //    m_bCodecAsMaster = MMP_TRUE;
    //}

    switch (m_ulCodecPath) {
    case AUDIO_EXT_HP_OUT:
        AudioInitializer(AUDIO_INIT_OUT);
        break;
    case AUDIO_EXT_MIC_IN:
        AudioInitializer(AUDIO_INIT_MICIN);
        break;
    case AUDIO_EXT_AUX_IN:
        AudioInitializer(AUDIO_INIT_LINEIN);
        break;
    case AUDIO_EXT_AUX_BYPASS_HP:
        AudioInitializer(AUDIO_INIT_OUT_LINEIN);
        break;
    default:
        return MMP_AUDIO_ERR_PARAMETER;
        break;
    }
    m_bCodecInitialized = MMP_TRUE;

    return MMP_ERR_NONE;
}

//*----------------------------------------------------------------------------
// function : MMPC_AudioExtCodec_Uninit
// input    : none
// output   : none
// descript : Uninitialize and power down codec according to path settings.
//*----------------------------------------------------------------------------
MMP_ERR MMPC_AudioExtCodec_Uninit(void)
{
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_TRUE);

    if (m_bCodecInitialized) {
        switch (m_ulCodecPath) {
        case AUDIO_EXT_HP_OUT:
            //PowerDownExtDAC();
            break;
        case AUDIO_EXT_MIC_IN:
            PowerDownExtMicIn();
            break;
        case AUDIO_EXT_AUX_IN:
            //PowerDownExtLineIn();
            break;
        case AUDIO_EXT_AUX_BYPASS_HP:
            //PowerDownExtDAC();
            //PowerDownExtLineIn();
            break;
        default:
            return MMP_AUDIO_ERR_PARAMETER;
            break;
        }
        m_bCodecInitialized = MMP_FALSE;
        //m_ulCodecPath = MMPF_AUDIO_EXT_PATH_MAX;
    }
    //MMPD_System_EnableClock(MMPD_SYSTEM_CLK_MODULE_VI, MMP_FALSE);

    return MMP_ERR_NONE;
}

//MMPF_I2CM_ATTRIBUTE uAudioI2cmAttribute =  {1, 0x18, 8, 8, 0, MMP_FALSE, MMP_FALSE, MMP_FALSE, MMP_FALSE, 0, 0, 3, MMPF_I2CM_SPEED_HW_250K, NULL, NULL}; // using BGPIO16/17 to control Conexant I2C

void MMPF_InitTLV320ADC3101(MMP_ULONG samplerate)
{
	//AITPS_PAD   pPAD  = AITC_BASE_PAD;
	//AITPS_GPIO  pGPIO = AITC_BASE_GPIO;
    AITPS_GBL   pGBL = AITC_BASE_GBL;
	//volatile MMP_UBYTE *REG_BASE_B = (volatile MMP_UBYTE *) (0x80000000);

    RTNA_DBG_Str(3, "  ===== MMPF_InitTLV320ADC3101 =====\r\n");       

	pGBL->GBL_CLK_DIS0 &= ~(GBL_CLK_VI_DIS);
	pGBL->GBL_CLK_DIS1 &= ~(GBL_CLK_I2C_DIS);
	//MMPF_I2cm_Initialize(&uAudioI2cmAttribute);	
    InitExtMicIn();

}

void MMPF_PowerUpTLV320ADC3101(MMP_USHORT sampling_rate)
{
	//AITPS_PAD   pPAD  = AITC_BASE_PAD;
	//AITPS_GPIO  pGPIO = AITC_BASE_GPIO;
	AITPS_GBL   pGBL = AITC_BASE_GBL;
    
    // sensor PD_12A disable
    //unsigned int clock=0;
    
	pGBL->GBL_CLK_DIS0 &= ~(GBL_CLK_VI_DIS);
	pGBL->GBL_CLK_DIS1 &= ~(GBL_CLK_I2C_DIS);
    //MMPF_OS_Sleep_MS(5);
	
	//MMPF_I2cm_Initialize(&uAudioI2cmAttribute);	
	PowerUpExtMicIn();
	//pGBL->GBL_CLK_EN  = clock;
	MMPF_OS_Sleep_MS(1);
}

void MMPF_PowerDownTLV320ADC3101(void)
{
	//AITPS_PAD   pPAD  = AITC_BASE_PAD;
	//AITPS_GPIO  pGPIO = AITC_BASE_GPIO;
	AITPS_GBL   pGBL = AITC_BASE_GBL;
	//unsigned int clock=0;
	
	pGBL->GBL_CLK_DIS0 &= ~(GBL_CLK_VI_DIS);
	pGBL->GBL_CLK_DIS1 &= ~(GBL_CLK_I2C_DIS);
	
	//MMPF_I2cm_Initialize(&uAudioI2cmAttribute);	
    PowerDownExtMicIn();

}

void MMPF_TLV320ADC3101VolumeControl(MMP_SHORT volume)
{
	AITPS_GBL   pGBL = AITC_BASE_GBL;
    //unsigned int clock=0;

	pGBL->GBL_CLK_DIS0 &= ~(GBL_CLK_VI_DIS);
	pGBL->GBL_CLK_DIS1 &= ~(GBL_CLK_I2C_DIS);

    //dbg_printf(0, "MMPF_TLV320ADC3101VolumeControl:0x%x\r\n", volume);
	//MMPF_I2cm_Initialize(&uAudioI2cmAttribute);	
	MMPC_AudioExtCodec_SetRecordVolume(volume);
	//pGBL->GBL_CLK_EN  = clock;
}

void MMPF_TLV320ADC3101_SetMute(MMP_UBYTE enable)
{
	AITPS_GBL   pGBL = AITC_BASE_GBL;
//    unsigned int clock=0, clock1 = 0;
	//MMP_USHORT  data = 0xFF;

//	clock =  pGBL->GBL_CLK_DIS0 ;
//	clock1 =  pGBL->GBL_CLK_DIS1 ;
	pGBL->GBL_CLK_DIS0 &= ~(GBL_CLK_VI_DIS);
	pGBL->GBL_CLK_DIS1 &= ~(GBL_CLK_I2C_DIS);

	//MMPF_I2cm_Initialize(&uAudioI2cmAttribute);	
    MMPC_AudioExtCodec_SetMute(enable);
	//pGBL->GBL_CLK_DIS0  = clock;
	//pGBL->GBL_CLK_DIS1  = clock;
}

#endif
#endif
#endif //end of SUPPORT_UAC